package com.zsk.okhttp;

import java.io.File;
import java.io.IOException;

import okhttp3.Callback;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import okio.Sink;

/**
 * multipart/form-data post task
 * 
 * @author zincredible
 * @date 2020/04/20 14:42:33
 */
public class PostMultiTask implements IPostMultiTask {

    protected OkHttpClient client;

    protected MultipartBody.Builder multiBodyBuilder;
    protected Request.Builder requestBuilder;
    protected HttpUrl.Builder urlBuilder;

    public PostMultiTask(OkHttpClient client, String url) {
        super();
        this.client = client;
        this.requestBuilder = new Request.Builder();
        this.urlBuilder = HttpUrl.parse(url).newBuilder();
        this.multiBodyBuilder = new MultipartBody.Builder();
    }

    @Override
    public ResData run(PercentListener listener) throws IOException {
        Request request = createRequest(listener);
        return ResData.of(client.newCall(request).execute());
    }

    @Override
    public void run(Callback callback, PercentListener listener) {
        Request request = createRequest(listener);
        client.newCall(request).enqueue(callback);
    }

    @Override
    public IPostMultiTask addHeader(String name, String value) {
        requestBuilder.addHeader(name, value);
        return this;
    }

    @Override
    public IPostMultiTask addFormDataPart(String name, String value) {
        multiBodyBuilder.addFormDataPart(name, value);
        return this;
    }

    @Override
    public IPostMultiTask addFormDataPart(String name, File file) {
        RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
        multiBodyBuilder.addFormDataPart(name, file.getName(), fileBody);
        return this;
    }

    @Override
    public IPostMultiTask addQueryParam(String name, String value) {
        urlBuilder.addQueryParameter(name, value);
        return this;
    }

    protected Request createRequest(PercentListener listener) {
        multiBodyBuilder.setType(MultipartBody.FORM);
        requestBuilder.url(urlBuilder.build()).post(new ProgressRequestBody(multiBodyBuilder.build(), listener))
            .build();
        Request request = requestBuilder.build();
        return request;
    }

    public static class ProgressRequestBody extends RequestBody {
        private RequestBody requestBody;
        private PercentListener mListener;
        private BufferedSink bufferedSink;

        public ProgressRequestBody(RequestBody body, PercentListener listener) {
            requestBody = body;
            mListener = listener;

        }

        @Override
        public MediaType contentType() {
            return requestBody.contentType();
        }

        @Override
        public long contentLength() throws IOException {
            return requestBody.contentLength();
        }

        @Override
        public void writeTo(BufferedSink sink) throws IOException {

            if (bufferedSink == null) {
                bufferedSink = Okio.buffer(sink(sink));
            }
            // 写入
            requestBody.writeTo(bufferedSink);
            // 刷新
            bufferedSink.flush();
        }

        private Sink sink(BufferedSink sink) {
            return new ForwardingSink(sink) {
                long bytesWritten = 0L;
                long contentLength = 0L;
                int prePercent = 0;

                @Override
                public void write(okio.Buffer source, long byteCount) throws IOException {
                    super.write(source, byteCount);
                    if (contentLength == 0) {
                        contentLength = contentLength();
                    }
                    bytesWritten += byteCount;
                    int percent = (int)(bytesWritten * 100.0 / contentLength);
                    if (percent > prePercent && mListener != null) {
                        mListener.process(percent, contentLength, bytesWritten, bytesWritten == contentLength);
                        prePercent = percent;
                    }
                }
            };
        }
    }

}
